﻿using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace HighPerformanceLog.V0
{
    /// <summary>
    /// 采用双缓冲和多线程机制的高性能日志处理类。
    /// </summary>
    public class HPLogWriter
    {
        /// <summary>
        /// 用于保存日志的文件。
        /// </summary>
        public string LogPath { get; private set; }

        /// <summary>
        /// 缓冲大小，默认为100K个日志记录。
        /// </summary>
        public int BufferSize { get; set; }

        // 用于控制线程启停
        bool isRunning = false;

        StreamWriter sw;

        // 主缓冲
        object[] mainBuffer;

        // 副缓冲
        object[] subBuffer;

        // 缓冲索引位置.
        int bufferIndex = 0;


        // 任务执行线程
        Thread thread;

        /// <summary>
        /// 新建类的对象。
        /// </summary>
        public HPLogWriter()
        {
            Initialize();
            File.Delete(LogPath);
        }



        /// <summary>
        /// 新建类的对象。
        /// </summary>
        /// <param name="logPath">Log记录文件.</param>
        /// <param name="bufferSize">缓冲大小，默认为100K个。</param>
        public HPLogWriter(string logPath, int bufferSize = 100000)
        {
            Initialize(logPath, bufferSize);
        }

        private void Initialize(string logpath = "system.log", int bufferSize = 100000)
        {
            LogPath = logpath;
            BufferSize = bufferSize;
            mainBuffer = new object[BufferSize];
            bufferIndex = 0;
        }

        /// <summary>
        /// 添加日志
        /// </summary>
        /// <param name="log">日志记录</param>
        public void Add(object log)
        {
            lock (this)
            {
                if (bufferIndex >= mainBuffer.Length)
                {
                    SwichBuffer();
                    Thread.Sleep(1);
                }

                mainBuffer[bufferIndex++] = log;
            }
        }

        /// <summary>
        /// 这个函数存在只是为了测试添加时的最大速度。
        /// </summary>
        /// <param name="log"></param>
        public void Add1(object log)
        {
            lock (this)
            {
                mainBuffer[bufferIndex++ % mainBuffer.Length] = log;
            }
        }

        /// <summary>
        /// 在主副缓冲之间切换。
        /// </summary>
        private void SwichBuffer()
        {
            // 如果正在保存，则需要等待保存结束。
            while (isSaving)
                Thread.Sleep(0);

            subBuffer = mainBuffer;
            mainBuffer = new object[BufferSize];
            bufferIndex = 0;
        }

        bool isSaving = false;
        double saveTime = 0;
        double toStringTime = 0;
        double initTime = 0;

        /// <summary>
        /// 对数据进行保存
        /// </summary>
        public void Save()
        {
            // 由于每次将副缓冲数据写入后会置空，所以可能为null
            if (subBuffer == null || subBuffer.Length == 0)
                return;
            isSaving = true;

            // 日志的长度一般不超过200
            DateTime dt1 = DateTime.Now;
            StringBuilder sb = new StringBuilder(BufferSize * 200);
            DateTime dt2 = DateTime.Now;
            initTime += (dt2 - dt1).TotalMilliseconds;

            // to string
            dt1 = DateTime.Now;
            string[] items = ToString(subBuffer);
            dt2 = DateTime.Now;
            toStringTime += (dt2 - dt1).TotalMilliseconds;

            // to file
            dt1 = DateTime.Now;
            for (int i = 0; i < items.Length; i++)
                sw.Write(items[i]);
            dt2 = DateTime.Now;
            saveTime += (dt2 - dt1).TotalMilliseconds;
            isSaving = false;
        }

        private string[] ToString(object[] objs, int section = 6)
        {
            LogToStringProcessor[] pros = new LogToStringProcessor[section];
            Thread[] threads = new Thread[pros.Length];

            int length = objs.Length / section;
            for (int i = 0; i < section; i++)
            {
                pros[i] = new LogToStringProcessor(objs, i * length, i == section - 1 ? objs.Length : (i + 1) * length);
                threads[i] = new Thread(new ThreadStart(pros[i].Process));
            }

            foreach (Thread thread in threads)
                thread.Start();

            foreach (Thread thread in threads)
                thread.Join();

            string[] items = new string[pros.Length];
            for (int i = 0; i < pros.Length; i++)
            {
                items[i] = pros[i].Result;
            }

            return items;
        }

        /// <summary>
        /// 任务主要处理方法
        /// </summary>
        private void Process()
        {
            isRunning = true;
            while (isRunning)
            {
                Save();
                Thread.Sleep(0);
            }
            SwichBuffer();
            Save();
            sw.Close();
            double total = initTime + toStringTime + saveTime;
            Console.WriteLine("Init Time: {0} ms, percent: {1}%.", initTime, (100 * initTime / total).ToString("0.00"));
            Console.WriteLine("ToString Time: {0} ms, percent: {1}%.", toStringTime, (100 * toStringTime / total).ToString("0.00"));
            Console.WriteLine("Save Time: {0} ms, percent: {1}%.", saveTime, (100 * saveTime / total).ToString("0.00"));
        }

        /// <summary>
        /// 启动服务
        /// </summary>
        public void Start()
        {
            if (thread != null && thread.IsAlive)
                return;

            sw = new StreamWriter(LogPath, true, Encoding.UTF8);
            thread = new Thread(Process);
            thread.Start();
        }

        /// <summary>
        /// 停止服务，并完成当前任务。
        /// </summary>
        public void Stop()
        {
            isRunning = false;
            thread.Join();
        }
    }

}
